From a504f48026f85c2133b21c82facf982697976ab7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 28 Jun 2016 10:39:46 -0700 Subject: [PATCH] Add flags to assert lock/cache behavior to Cargo If a lock file is generated and some equivalent of `cargo fetch` is run then Cargo shouldn't ever touch the network or modify `Cargo.lock` until any `Cargo.toml` later changes, but this often wants to be asserted in some build environments where it's a programmer error if Cargo attempts to access the network. The `--locked` flag added here will assert that `Cargo.lock` does not need to change to proceed. That is, if `Cargo.lock` would be modified (as it automatically is by default) this is turned into a hard error instead. This `--frozen` will not only assert that `Cargo.lock` doesn't change (the same behavior as `--locked`), but it will also will manually prevent Cargo from touching the network by ensuring that all network requests return an error. These flags can be used in environments where it is *expected* that no network access happens (or no lockfile changes happen) because it has been pre-arranged for Cargo to not happen. Examples of this include: * CI for projects want to pass `--locked` to ensure that `Cargo.lock` is up to date before changes are checked in. * Environments with vendored dependencies want to pass `--frozen` as touching the network indicates a programmer error that something wasn't vendored correctly. A crucial property of these two flags is that **they do not change the behavior of Cargo**. They are simply assertions at a few locations in Cargo to ensure that actions expected to not happen indeed don't happen. Some documentation has also been added to this effect. Closes #2111 --- src/bin/bench.rs | 12 +++++++++--- src/bin/build.rs | 12 +++++++++--- src/bin/cargo.rs | 22 +++++++++++++++++++--- src/bin/clean.rs | 12 +++++++++--- src/bin/doc.rs | 12 +++++++++--- src/bin/fetch.rs | 12 +++++++++--- src/bin/generate_lockfile.rs | 12 +++++++++--- src/bin/git_checkout.rs | 12 +++++++++--- src/bin/init.rs | 12 +++++++++--- src/bin/install.rs | 12 +++++++++--- src/bin/login.rs | 12 +++++++++--- src/bin/metadata.rs | 12 +++++++++--- src/bin/new.rs | 12 +++++++++--- src/bin/owner.rs | 12 +++++++++--- src/bin/package.rs | 12 +++++++++--- src/bin/pkgid.rs | 12 +++++++++--- src/bin/publish.rs | 12 +++++++++--- src/bin/run.rs | 12 +++++++++--- src/bin/rustc.rs | 12 +++++++++--- src/bin/rustdoc.rs | 12 +++++++++--- src/bin/search.rs | 14 ++++++++++---- src/bin/test.rs | 12 +++++++++--- src/bin/uninstall.rs | 12 +++++++++--- src/bin/update.rs | 12 +++++++++--- src/bin/verify_project.rs | 12 +++++++++--- src/bin/yank.rs | 12 +++++++++--- src/cargo/core/registry.rs | 5 ++++- src/cargo/ops/lockfile.rs | 6 ++++++ src/cargo/ops/registry.rs | 5 +++++ src/cargo/sources/git/utils.rs | 15 +++++++++++---- src/cargo/sources/registry.rs | 7 +++++-- src/cargo/util/config.rs | 24 ++++++++++++++++++++---- src/doc/faq.md | 34 ++++++++++++++++++++++++++++++++++ tests/bad-config.rs | 8 ++++++-- tests/build-auth.rs | 5 ++++- tests/overrides.rs | 5 ++++- tests/path.rs | 5 ++++- tests/registry.rs | 31 +++++++++++++++++++++++++++++++ 38 files changed, 379 insertions(+), 95 deletions(-) diff --git a/src/bin/bench.rs b/src/bin/bench.rs index 3db26741f..c7cf6b693 100644 --- a/src/bin/bench.rs +++ b/src/bin/bench.rs @@ -20,6 +20,8 @@ pub struct Options { flag_example: Vec, flag_test: Vec, flag_bench: Vec, + flag_frozen: bool, + flag_locked: bool, arg_args: Vec, } @@ -46,6 +48,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date All of the trailing arguments are passed to the benchmark binaries generated for filtering benchmarks and generally providing options configuring how they @@ -64,9 +68,11 @@ Compilation can be customized with the `bench` profile in the manifest. pub fn execute(options: Options, config: &Config) -> CliResult> { let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let ops = ops::TestOptions { no_run: options.flag_no_run, diff --git a/src/bin/build.rs b/src/bin/build.rs index 3fc0fe32e..601344adf 100644 --- a/src/bin/build.rs +++ b/src/bin/build.rs @@ -23,6 +23,8 @@ pub struct Options { flag_example: Vec, flag_test: Vec, flag_bench: Vec, + flag_locked: bool, + flag_frozen: bool, } pub const USAGE: &'static str = " @@ -48,6 +50,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date If the --package argument is given, then SPEC is a package id specification which indicates which package should be built. If it is not given, then the @@ -62,9 +66,11 @@ the --release flag will use the `release` profile instead. pub fn execute(options: Options, config: &Config) -> CliResult> { debug!("executing; cmd=cargo-build; args={:?}", env::args().collect::>()); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index cdd110f3c..d29a3cbb0 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -28,6 +28,8 @@ pub struct Flags { flag_explain: Option, arg_command: String, arg_args: Vec, + flag_locked: bool, + flag_frozen: bool, } const USAGE: &'static str = " @@ -45,6 +47,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date Some common cargo commands are (see all commands with --list): build Compile the current project @@ -68,6 +72,16 @@ fn main() { execute_main_without_stdin(execute, true, USAGE) } +macro_rules! configure_shell { + ($config:expr, $options:expr) => ( + try!($config.configure($options.flag_verbose, + $options.flag_quiet, + &$options.flag_color, + $options.flag_frozen, + $options.flag_locked)); + ) +} + macro_rules! each_subcommand{ ($mac:ident) => { $mac!(bench); @@ -113,9 +127,11 @@ each_subcommand!(declare_mod); on this top-level information. */ fn execute(flags: Flags, config: &Config) -> CliResult> { - try!(config.configure_shell(flags.flag_verbose, - flags.flag_quiet, - &flags.flag_color)); + try!(config.configure(flags.flag_verbose, + flags.flag_quiet, + &flags.flag_color, + flags.flag_frozen, + flags.flag_locked)); init_git_transports(config); cargo::util::job::setup(); diff --git a/src/bin/clean.rs b/src/bin/clean.rs index 36ec7db17..c259e83c6 100644 --- a/src/bin/clean.rs +++ b/src/bin/clean.rs @@ -14,6 +14,8 @@ pub struct Options { flag_quiet: Option, flag_color: Option, flag_release: bool, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -31,6 +33,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date If the --package argument is given, then SPEC is a package id specification which indicates which package's artifacts should be cleaned out. If it is not @@ -40,9 +44,11 @@ and its format, see the `cargo help pkgid` command. pub fn execute(options: Options, config: &Config) -> CliResult> { debug!("executing; cmd=cargo-clean; args={:?}", env::args().collect::>()); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); let opts = ops::CleanOptions { diff --git a/src/bin/doc.rs b/src/bin/doc.rs index 1c420def8..7f4fc2878 100644 --- a/src/bin/doc.rs +++ b/src/bin/doc.rs @@ -19,6 +19,8 @@ pub struct Options { flag_package: Vec, flag_lib: bool, flag_bin: Vec, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -43,6 +45,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date By default the documentation for the local package and all dependencies is built. The output is all placed in `target/doc` in rustdoc's usual format. @@ -54,9 +58,11 @@ the `cargo help pkgid` command. "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); diff --git a/src/bin/fetch.rs b/src/bin/fetch.rs index 2870dc63a..1a970b71f 100644 --- a/src/bin/fetch.rs +++ b/src/bin/fetch.rs @@ -9,6 +9,8 @@ pub struct Options { flag_verbose: u32, flag_quiet: Option, flag_color: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -23,6 +25,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date If a lockfile is available, this command will ensure that all of the git dependencies and/or registries dependencies are downloaded and locally @@ -35,9 +39,11 @@ all updated. "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); let ws = try!(Workspace::new(&root, config)); try!(ops::fetch(&ws)); diff --git a/src/bin/generate_lockfile.rs b/src/bin/generate_lockfile.rs index a49930fd9..36057c1bb 100644 --- a/src/bin/generate_lockfile.rs +++ b/src/bin/generate_lockfile.rs @@ -11,6 +11,8 @@ pub struct Options { flag_verbose: u32, flag_quiet: Option, flag_color: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -25,13 +27,17 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { debug!("executing; cmd=cargo-generate-lockfile; args={:?}", env::args().collect::>()); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); let ws = try!(Workspace::new(&root, config)); diff --git a/src/bin/git_checkout.rs b/src/bin/git_checkout.rs index d3f49db6c..e67844cce 100644 --- a/src/bin/git_checkout.rs +++ b/src/bin/git_checkout.rs @@ -9,6 +9,8 @@ pub struct Options { flag_verbose: u32, flag_quiet: Option, flag_color: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -23,12 +25,16 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let Options { flag_url: url, flag_reference: reference, .. } = options; let url = try!(url.to_url().map_err(|e| { diff --git a/src/bin/init.rs b/src/bin/init.rs index 3f02f057f..a779ec7e1 100644 --- a/src/bin/init.rs +++ b/src/bin/init.rs @@ -12,6 +12,8 @@ pub struct Options { arg_path: Option, flag_name: Option, flag_vcs: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -31,13 +33,17 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { debug!("executing; cmd=cargo-init; args={:?}", env::args().collect::>()); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let Options { flag_bin, arg_path, flag_name, flag_vcs, .. } = options; diff --git a/src/bin/install.rs b/src/bin/install.rs index 7f9ed9519..78d8f58fd 100644 --- a/src/bin/install.rs +++ b/src/bin/install.rs @@ -16,6 +16,8 @@ pub struct Options { flag_root: Option, flag_list: bool, flag_force: bool, + flag_frozen: bool, + flag_locked: bool, arg_crate: Option, flag_vers: Option, @@ -56,6 +58,8 @@ Build and install options: -v, --verbose ... Use verbose output -q, --quiet Less output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date This command manages Cargo's local set of installed binary crates. Only packages which have [[bin]] targets can be installed, and all binaries are installed into @@ -89,9 +93,11 @@ The `--list` option will list all installed packages (and their versions). "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let compile_opts = ops::CompileOptions { config: config, diff --git a/src/bin/login.rs b/src/bin/login.rs index 50d8249be..635321c57 100644 --- a/src/bin/login.rs +++ b/src/bin/login.rs @@ -13,6 +13,8 @@ pub struct Options { flag_verbose: u32, flag_quiet: Option, flag_color: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -27,13 +29,17 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let token = match options.arg_token.clone() { Some(token) => token, None => { diff --git a/src/bin/metadata.rs b/src/bin/metadata.rs index d8a9f1fe5..d5c2d0905 100644 --- a/src/bin/metadata.rs +++ b/src/bin/metadata.rs @@ -13,6 +13,8 @@ pub struct Options { flag_no_deps: bool, flag_quiet: Option, flag_verbose: u32, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -34,12 +36,16 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let manifest = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); let options = OutputMetadataOptions { diff --git a/src/bin/new.rs b/src/bin/new.rs index 70f9018e4..e90194f1d 100644 --- a/src/bin/new.rs +++ b/src/bin/new.rs @@ -12,6 +12,8 @@ pub struct Options { arg_path: String, flag_name: Option, flag_vcs: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -31,13 +33,17 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { debug!("executing; cmd=cargo-new; args={:?}", env::args().collect::>()); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let Options { flag_bin, arg_path, flag_name, flag_vcs, .. } = options; diff --git a/src/bin/owner.rs b/src/bin/owner.rs index 1eb024295..9b666adfe 100644 --- a/src/bin/owner.rs +++ b/src/bin/owner.rs @@ -12,6 +12,8 @@ pub struct Options { flag_quiet: Option, flag_color: Option, flag_list: bool, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -30,6 +32,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date This command will modify the owners for a package on the specified registry (or default). Note that owners of a package can upload new versions, yank old @@ -41,9 +45,11 @@ and troubleshooting. "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let opts = ops::OwnersOptions { krate: options.arg_crate, token: options.flag_token, diff --git a/src/bin/package.rs b/src/bin/package.rs index 4a4540854..40eb7bac7 100644 --- a/src/bin/package.rs +++ b/src/bin/package.rs @@ -14,6 +14,8 @@ pub struct Options { flag_list: bool, flag_allow_dirty: bool, flag_jobs: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -33,12 +35,16 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); let ws = try!(Workspace::new(&root, config)); try!(ops::package(&ws, &ops::PackageOpts { diff --git a/src/bin/pkgid.rs b/src/bin/pkgid.rs index 963caf11f..13aedf257 100644 --- a/src/bin/pkgid.rs +++ b/src/bin/pkgid.rs @@ -9,6 +9,8 @@ pub struct Options { flag_quiet: Option, flag_color: Option, flag_manifest_path: Option, + flag_frozen: bool, + flag_locked: bool, arg_spec: Option, } @@ -24,6 +26,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date Given a argument, print out the fully qualified package id specifier. This command will generate an error if is ambiguous as to which package @@ -48,9 +52,11 @@ Example Package IDs pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path.clone(), config.cwd())); let ws = try!(Workspace::new(&root, config)); diff --git a/src/bin/publish.rs b/src/bin/publish.rs index f5a9d8277..7c2770460 100644 --- a/src/bin/publish.rs +++ b/src/bin/publish.rs @@ -15,6 +15,8 @@ pub struct Options { flag_allow_dirty: bool, flag_jobs: Option, flag_dry_run: bool, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -35,13 +37,17 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let Options { flag_token: token, flag_host: host, diff --git a/src/bin/run.rs b/src/bin/run.rs index 378e3dc24..b889087d6 100644 --- a/src/bin/run.rs +++ b/src/bin/run.rs @@ -16,6 +16,8 @@ pub struct Options { flag_quiet: Option, flag_color: Option, flag_release: bool, + flag_frozen: bool, + flag_locked: bool, arg_args: Vec, } @@ -38,6 +40,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date If neither `--bin` nor `--example` are given, then if the project only has one bin target it will be run. Otherwise `--bin` specifies the bin target to run, @@ -50,9 +54,11 @@ the ones before go to Cargo. "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); diff --git a/src/bin/rustc.rs b/src/bin/rustc.rs index cce7a12d8..10fd9fc91 100644 --- a/src/bin/rustc.rs +++ b/src/bin/rustc.rs @@ -25,6 +25,8 @@ pub struct Options { flag_test: Vec, flag_bench: Vec, flag_profile: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -51,6 +53,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date The specified target for the current package (or package specified by SPEC if provided) will be compiled along with all of its dependencies. The specified @@ -69,9 +73,11 @@ processes spawned by Cargo, use the $RUSTFLAGS environment variable or the pub fn execute(options: Options, config: &Config) -> CliResult> { debug!("executing; cmd=cargo-rustc; args={:?}", env::args().collect::>()); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); diff --git a/src/bin/rustdoc.rs b/src/bin/rustdoc.rs index c5710a184..010b003c5 100644 --- a/src/bin/rustdoc.rs +++ b/src/bin/rustdoc.rs @@ -22,6 +22,8 @@ pub struct Options { flag_example: Vec, flag_test: Vec, flag_bench: Vec, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -48,6 +50,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date The specified target for the current package (or package specified by SPEC if provided) will be documented with the specified ... being passed to the @@ -63,9 +67,11 @@ the `cargo help pkgid` command. "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); diff --git a/src/bin/search.rs b/src/bin/search.rs index 4d1f1ea3b..829039aaa 100644 --- a/src/bin/search.rs +++ b/src/bin/search.rs @@ -10,7 +10,9 @@ pub struct Options { flag_quiet: Option, flag_color: Option, flag_limit: Option, - arg_query: Vec + flag_frozen: bool, + flag_locked: bool, + arg_query: Vec, } pub const USAGE: &'static str = " @@ -27,12 +29,16 @@ Options: -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never --limit LIMIT Limit the number of results (default: 10, max: 100) + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let Options { flag_host: host, flag_limit: limit, diff --git a/src/bin/test.rs b/src/bin/test.rs index f7f53bc4b..cd823a591 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -24,6 +24,8 @@ pub struct Options { flag_color: Option, flag_release: bool, flag_no_fail_fast: bool, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -52,6 +54,8 @@ Options: -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never --no-fail-fast Run all tests regardless of failure + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date All of the trailing arguments are passed to the test binaries generated for filtering tests and generally providing options configuring how they run. For @@ -81,9 +85,11 @@ To get the list of all options available for the test binaries use this: "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); let empty = Vec::new(); diff --git a/src/bin/uninstall.rs b/src/bin/uninstall.rs index 046997a44..001abde41 100644 --- a/src/bin/uninstall.rs +++ b/src/bin/uninstall.rs @@ -8,6 +8,8 @@ pub struct Options { flag_verbose: u32, flag_quiet: Option, flag_color: Option, + flag_frozen: bool, + flag_locked: bool, arg_spec: String, } @@ -26,6 +28,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet Less output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date The argument SPEC is a package id specification (see `cargo help pkgid`) to specify which crate should be uninstalled. By default all binaries are @@ -34,9 +38,11 @@ only uninstall particular binaries. "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = options.flag_root.as_ref().map(|s| &s[..]); try!(ops::uninstall(root, &options.arg_spec, &options.flag_bin, config)); diff --git a/src/bin/update.rs b/src/bin/update.rs index 8c72dd111..6d1d7935b 100644 --- a/src/bin/update.rs +++ b/src/bin/update.rs @@ -14,6 +14,8 @@ pub struct Options { flag_verbose: u32, flag_quiet: Option, flag_color: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -31,6 +33,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date This command requires that a `Cargo.lock` already exists as generated by `cargo build` or related commands. @@ -55,9 +59,11 @@ For more information about package id specifications, see `cargo help pkgid`. pub fn execute(options: Options, config: &Config) -> CliResult> { debug!("executing; cmd=cargo-update; args={:?}", env::args().collect::>()); - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())); let update_opts = ops::UpdateOptions { diff --git a/src/bin/verify_project.rs b/src/bin/verify_project.rs index 0ce407e08..27424f7a6 100644 --- a/src/bin/verify_project.rs +++ b/src/bin/verify_project.rs @@ -16,6 +16,8 @@ pub struct Flags { flag_verbose: u32, flag_quiet: Option, flag_color: Option, + flag_frozen: bool, + flag_locked: bool, } pub const USAGE: &'static str = " @@ -31,12 +33,16 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date "; pub fn execute(args: Flags, config: &Config) -> CliResult> { - try!(config.configure_shell(args.flag_verbose, - args.flag_quiet, - &args.flag_color)); + try!(config.configure(args.flag_verbose, + args.flag_quiet, + &args.flag_color, + args.flag_frozen, + args.flag_locked)); let mut contents = String::new(); let filename = args.flag_manifest_path.unwrap_or("Cargo.toml".into()); diff --git a/src/bin/yank.rs b/src/bin/yank.rs index a869f36ff..7971efbb3 100644 --- a/src/bin/yank.rs +++ b/src/bin/yank.rs @@ -11,6 +11,8 @@ pub struct Options { flag_quiet: Option, flag_color: Option, flag_undo: bool, + flag_frozen: bool, + flag_locked: bool, } pub static USAGE: &'static str = " @@ -28,6 +30,8 @@ Options: -v, --verbose ... Use verbose output -q, --quiet No output printed to stdout --color WHEN Coloring: auto, always, never + --frozen Require Cargo.lock and cache are up to date + --locked Require Cargo.lock is up to date The yank command removes a previously pushed crate's version from the server's index. This command does not delete any data, and the crate will still be @@ -39,9 +43,11 @@ crates to be locked to any yanked version. "; pub fn execute(options: Options, config: &Config) -> CliResult> { - try!(config.configure_shell(options.flag_verbose, - options.flag_quiet, - &options.flag_color)); + try!(config.configure(options.flag_verbose, + options.flag_quiet, + &options.flag_color, + options.flag_frozen, + options.flag_locked)); try!(ops::yank(config, options.arg_crate, options.flag_vers, diff --git a/src/cargo/core/registry.rs b/src/cargo/core/registry.rs index 1ecf4406d..ccfe87cab 100644 --- a/src/cargo/core/registry.rs +++ b/src/cargo/core/registry.rs @@ -269,7 +269,10 @@ impl<'cfg> Registry for PackageRegistry<'cfg> { let ret = if overrides.is_empty() { // Ensure the requested source_id is loaded - try!(self.ensure_loaded(dep.source_id(), Kind::Normal)); + try!(self.ensure_loaded(dep.source_id(), Kind::Normal).chain_error(|| { + human(format!("failed to load source for a dependency \ + on `{}`", dep.name())) + })); match self.sources.get_mut(dep.source_id()) { Some(src) => try!(src.query(&dep)), diff --git a/src/cargo/ops/lockfile.rs b/src/cargo/ops/lockfile.rs index f0a1f72c3..91a2bf2d7 100644 --- a/src/cargo/ops/lockfile.rs +++ b/src/cargo/ops/lockfile.rs @@ -86,6 +86,12 @@ pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()> } } + if !ws.config().lock_update_allowed() { + let flag = if ws.config().network_allowed() {"--frozen"} else {"--locked"}; + bail!("the lock file needs to be updated but {} was passed to \ + prevent this", flag); + } + // Ok, if that didn't work just write it out root.open_rw("Cargo.lock", ws.config(), "Cargo.lock file").and_then(|mut f| { try!(f.file().set_len(0)); diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index ac831b357..e0d4fd5d6 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -182,6 +182,11 @@ pub fn registry(config: &Config, /// Create a new HTTP handle with appropriate global configuration for cargo. pub fn http_handle(config: &Config) -> CargoResult { + if !config.network_allowed() { + bail!("attempting to make an HTTP request, but --frozen was \ + specified") + } + // The timeout option for libcurl by default times out the entire transfer, // but we probably don't want this. Instead we only set timeouts for the // connect phase as well as a "low speed" timeout so if we don't receive diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 7ffdddade..1149bfffd 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -560,19 +560,26 @@ fn with_authentication(url: &str, cfg: &git2::Config, mut f: F) }) } -pub fn fetch(repo: &git2::Repository, url: &str, - refspec: &str, cargo_config: &Config) -> CargoResult<()> { - // Create a local anonymous remote in the repository to fetch the url +pub fn fetch(repo: &git2::Repository, + url: &str, + refspec: &str, + config: &Config) -> CargoResult<()> { + if !config.network_allowed() { + bail!("attempting to update a git repository, but --frozen \ + was specified") + } with_authentication(url, &try!(repo.config()), |f| { let mut cb = git2::RemoteCallbacks::new(); cb.credentials(f); + + // Create a local anonymous remote in the repository to fetch the url let mut remote = try!(repo.remote_anonymous(&url)); let mut opts = git2::FetchOptions::new(); opts.remote_callbacks(cb) .download_tags(git2::AutotagOption::All); - try!(network::with_retry(cargo_config, ||{ + try!(network::with_retry(config, ||{ remote.fetch(&[refspec], Some(&mut opts), None) })); Ok(()) diff --git a/src/cargo/sources/registry.rs b/src/cargo/sources/registry.rs index 06e6344bb..f89f6bae7 100644 --- a/src/cargo/sources/registry.rs +++ b/src/cargo/sources/registry.rs @@ -177,6 +177,7 @@ use core::dependency::{Dependency, DependencyInner, Kind}; use sources::{PathSource, git}; use util::{CargoResult, Config, internal, ChainError, ToUrl, human}; use util::{hex, Sha256, paths, Filesystem, FileLock}; +use util::network; use ops; const DEFAULT: &'static str = "https://github.com/rust-lang/crates.io-index"; @@ -315,7 +316,9 @@ impl<'cfg> RegistrySource<'cfg> { body.extend_from_slice(buf); Ok(buf.len()) })); - try!(handle.perform()); + try!(network::with_retry(self.config, || { + handle.perform() + })) } let code = try!(handle.response_code()); if code != 200 && code != 0 { @@ -495,7 +498,7 @@ impl<'cfg> RegistrySource<'cfg> { let refspec = "refs/heads/*:refs/remotes/origin/*"; try!(git::fetch(&repo, &url, refspec, &self.config).chain_error(|| { - internal(format!("failed to fetch `{}`", url)) + human(format!("failed to fetch `{}`", url)) })); // git reset --hard origin/master diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 37bdf0357..d1b299e5f 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -32,6 +32,8 @@ pub struct Config { rustdoc: PathBuf, target_dir: RefCell>, extra_verbose: Cell, + frozen: Cell, + locked: Cell, } impl Config { @@ -49,6 +51,8 @@ impl Config { rustdoc: PathBuf::from("rustdoc"), target_dir: RefCell::new(None), extra_verbose: Cell::new(false), + frozen: Cell::new(false), + locked: Cell::new(false), }; try!(cfg.scrape_tool_config()); @@ -292,10 +296,12 @@ impl Config { }) } - pub fn configure_shell(&self, - verbose: u32, - quiet: Option, - color: &Option) -> CargoResult<()> { + pub fn configure(&self, + verbose: u32, + quiet: Option, + color: &Option, + frozen: bool, + locked: bool) -> CargoResult<()> { let extra_verbose = verbose >= 2; let verbose = if verbose == 0 {None} else {Some(true)}; let cfg_verbose = try!(self.get_bool("term.verbose")).map(|v| v.val); @@ -329,6 +335,8 @@ impl Config { self.shell().set_verbosity(verbosity); try!(self.shell().set_color_config(color.map(|s| &s[..]))); self.extra_verbose.set(extra_verbose); + self.frozen.set(frozen); + self.locked.set(locked); Ok(()) } @@ -337,6 +345,14 @@ impl Config { self.extra_verbose.get() } + pub fn network_allowed(&self) -> bool { + !self.frozen.get() + } + + pub fn lock_update_allowed(&self) -> bool { + !self.frozen.get() && !self.locked.get() + } + fn load_values(&self) -> CargoResult<()> { let mut cfg = CV::Table(HashMap::new(), PathBuf::from(".")); diff --git a/src/doc/faq.md b/src/doc/faq.md index ae7e9bcaa..7782247ce 100644 --- a/src/doc/faq.md +++ b/src/doc/faq.md @@ -155,3 +155,37 @@ many possible names has historically led to confusion where one case was handled but others were accidentally forgotten. [crates.io]: https://crates.io/ + +# How can Cargo work offline? + +Cargo is often used in situations with limited or no network access such as +airplanes, CI environments, or embedded in large production deployments. Users +are often surprised when Cargo attempts to fetch resources from the network, and +hence the request for Cargo to work offline comes up frequently. + +Cargo, at its heart, will not attempt to access the network unless told to do +so. That is, if no crates comes from crates.io, a git repository, or some other +network location, Cargo will never attempt to make a network connection. As a +result, if Cargo attempts to touch the network, then it's because it needs to +fetch a required resource. + +Cargo is also quite aggressive about caching information to minimize the amount +of network activity. It will guarantee, for example, that if `cargo build` (or +an equivalent) is run to completion then the next `cargo build` is guaranteed to +not touch the network so long as `Cargo.toml` has not been modified in the +meantime. This avoidance of the network boils down to a `Cargo.lock` existing +and a populated cache of the crates reflected in the lock file. If either of +these components are missing, then they're required for the build to succeed and +must be fetched remotely. + +As of Rust 1.11.0 (to be released 2016-09-29) Cargo understands a new flag, +`--frozen`, which is an assertion that it shouldn't touch the network. When +passed, Cargo will immediately return an error if it would otherwise attempt a +network request. The error should include contextual information about why the +network request is being made in the first place to help debug as well. Note +that this flag *does not change the behavior of Cargo*, it simply asserts that +Cargo shouldn't touch the network as a previous command has been run to ensure +that network activity shouldn't be necessary. + +Note that Cargo does not yet support vendoring in a first-class fashion, but +this is a hotly desired feature and coming soon! diff --git a/tests/bad-config.rs b/tests/bad-config.rs index 880ba85ba..3b806ef46 100644 --- a/tests/bad-config.rs +++ b/tests/bad-config.rs @@ -73,9 +73,10 @@ fn bad3() { [http] proxy = true "#); + Package::new("foo", "1.0.0").publish(); assert_that(foo.cargo_process("publish").arg("-v"), execs().with_status(101).with_stderr("\ -[UPDATING] registry `https://[..]` +[UPDATING] registry `[..]` [ERROR] invalid configuration for key `http.proxy` expected a string, but found a boolean in [..]config ")); @@ -246,7 +247,10 @@ fn bad_git_dependency() { assert_that(foo.cargo_process("build").arg("-v"), execs().with_status(101).with_stderr("\ [UPDATING] git repository `file:///` -[ERROR] Unable to update file:/// +[ERROR] failed to load source for a dependency on `foo` + +Caused by: + Unable to update file:/// Caused by: failed to clone into: [..] diff --git a/tests/build-auth.rs b/tests/build-auth.rs index 6ac2107df..8784329a9 100644 --- a/tests/build-auth.rs +++ b/tests/build-auth.rs @@ -102,7 +102,10 @@ fn http_auth_offered() { assert_that(p.cargo_process("build"), execs().with_status(101).with_stderr(&format!("\ [UPDATING] git repository `http://{addr}/foo/bar` -[ERROR] Unable to update http://{addr}/foo/bar +[ERROR] failed to load source for a dependency on `bar` + +Caused by: + Unable to update http://{addr}/foo/bar Caused by: failed to clone into: [..] diff --git a/tests/overrides.rs b/tests/overrides.rs index 46f118a44..7157bb5f1 100644 --- a/tests/overrides.rs +++ b/tests/overrides.rs @@ -467,7 +467,10 @@ fn override_with_nothing() { execs().with_status(101).with_stderr("\ [UPDATING] registry [..] [UPDATING] git repository [..] -error: Unable to update file://[..] +[ERROR] failed to load source for a dependency on `foo` + +Caused by: + Unable to update file://[..] Caused by: Could not find Cargo.toml in `[..]` diff --git a/tests/path.rs b/tests/path.rs index 574851e93..0e8aa25c9 100644 --- a/tests/path.rs +++ b/tests/path.rs @@ -524,7 +524,10 @@ fn error_message_for_missing_manifest() { assert_that(p.cargo_process("build"), execs().with_status(101) .with_stderr("\ -[ERROR] Unable to update file://[..] +[ERROR] failed to load source for a dependency on `bar` + +Caused by: + Unable to update file://[..] Caused by: failed to read `[..]bar[..]Cargo.toml` diff --git a/tests/registry.rs b/tests/registry.rs index 7f600fb17..1a2371bfd 100644 --- a/tests/registry.rs +++ b/tests/registry.rs @@ -1070,3 +1070,34 @@ fn upstream_warnings_on_extra_verbose() { [..]warning: function is never used[..] ")); } + +#[test] +fn disallow_network() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "*" + "#) + .file("src/main.rs", "fn main() {}"); + p.build(); + + assert_that(p.cargo("build").arg("--frozen"), + execs().with_status(101).with_stderr("\ +[UPDATING] registry `[..]` +error: failed to load source for a dependency on `foo` + +Caused by: + Unable to update registry [..] + +Caused by: + failed to fetch `[..]` + +Caused by: + attempting to update a git repository, but --frozen was specified +")); +} -- 2.30.2